/*
 * Copyright (C) 2013 gujicheng
 * 
 * Licensed under the GPL License Version 2.0;
 * you may not use this file except in compliance with the License.
 * 
 * If you have any question, please contact me.
 * 
 *************************************************************************
 **                   Author information                                **
 *************************************************************************
 ** Email: gujicheng197@126.com                                         **
 ** QQ   : 29600731                                                     **
 ** Weibo: http://weibo.com/gujicheng197                                **
 *************************************************************************
 */
package com.libra.sinvoice

import com.libra.sinvoice.Buffer.BufferData
import kotlin.experimental.*

class VoiceRecognition(private val mCallback: Callback?, private val mSampleRate: Int, private val mChannel: Int,
                       private val mBits: Int) {
    companion object {
        private val TAG = "Recognition"
        private val STATE_START = 1
        private val STATE_STOP = 2
        private val STATE_STEP1 = 1
        private val STATE_STEP2 = 2
        private val MAX_CIRCLE = 31
        private val MIN_CIRCLE = 10
        private val MIN_CIRCLE_REPEAT = 8
    }

    private var mState: Int = 0
    private var mListener: Listener? = null

    private var mCirclePointCount = 0

    private var mIsStartCounting = false
    private var mStep: Int = 0
    private var mIsBeginning = false
    private var mStartingDet = false
    private var mStartingDetCount: Int = 0

    private var mRegValue: Int = 0
    private var mRegedChar: Int = 0
    private var mRegedCharLast: Int = 0
    private var mRegedCharCount: Int = 0
    private var mRegedCharAddedCount: Int = 0
    private var mRegIndexLast: Int = 0
    private var mRegCount: Int = 0
    private var mPreRegCircle: Int = 0
    private var mIsRegStart = false

    interface Listener {
        fun onStartRecognition()
        fun onRecognition(index: Int)
        fun onStopRecognition()
    }

    interface Callback {
        val recognitionBuffer: BufferData?

        fun freeRecognitionBuffer(buffer: BufferData)
    }

    init {
        mState = STATE_STOP
    }

    fun setListener(listener: Listener) {
        mListener = listener
    }

    fun start() {
        if (STATE_STOP == mState) {
            mState = STATE_START

            if (null != mCallback) {
                mCirclePointCount = 0

                mIsStartCounting = false
                mStep = STATE_STEP1
                mIsBeginning = false
                mStartingDet = false
                mStartingDetCount = 0
                mPreRegCircle = -1
                if (null != mListener) {
                    mListener!!.onStartRecognition()
                }
                while (STATE_START == mState) {
                    val data = mCallback.recognitionBuffer
                    if (null != data) {
                        if (null != data.byteData) {
                            process(data)

                            mCallback.freeRecognitionBuffer(data)
                        } else {
                            LogHelper.d(TAG, "end input buffer, so stop")
                            break
                        }
                    } else {
                        LogHelper.d(TAG, "get null recognition buffer")
                        break
                    }
                }

                if (null != mListener) {
                    mListener!!.onStopRecognition()
                }
            }
        }
    }

    fun stop() {
        if (STATE_START == mState) {
            mState = STATE_STOP
        }
    }

    private fun process(data: BufferData) {
        val size = data.filledSize - 1
        var sh: Short = 0
        var i = 0
        while (i < size) {
            var sh1 = data.byteData!![i].toShort()
            sh1 = sh1 and 0xff
            val sh2 = (data.byteData!![++i].toInt() shl 8).toShort()
            sh = sh1 or sh2

            if (!mIsStartCounting) {
                if (STATE_STEP1 == mStep) {
                    if (sh < 0) {
                        mStep = STATE_STEP2
                    }
                } else if (STATE_STEP2 == mStep) {
                    if (sh > 0) {
                        mIsStartCounting = true
                        mCirclePointCount = 0
                        mStep = STATE_STEP1
                    }
                }
            } else {
                ++mCirclePointCount
                if (STATE_STEP1 == mStep) {
                    if (sh < 0) {
                        mStep = STATE_STEP2
                    }
                } else if (STATE_STEP2 == mStep) {
                    if (sh > 0) {
                        // preprocess the circle
                        val circleCount = preReg(mCirclePointCount)

                        // recognise voice
                        reg(circleCount, i)

                        mCirclePointCount = 0
                        mStep = STATE_STEP1
                    }
                }
            }
            i++
        }
    }

    private fun preReg(circleCountOrg: Int): Int {
        var circleCount = circleCountOrg
        when (circleCount) {
            8, 9, 10, 11, 12 -> circleCount = 10

            13, 14, 15, 16, 17 -> circleCount = 15

            18, 19, 20 -> circleCount = 19

            21, 22, 23 -> circleCount = 22

            24, 25, 26 -> circleCount = 25

            27, 28, 29 -> circleCount = 28

            30, 31, 32 -> circleCount = 31

            else -> circleCount = 0
        }

        return circleCount
    }

    private fun reg(circleCount: Int, idx: Int) {
        if (!mIsBeginning) {
            if (!mStartingDet) {
                if (MAX_CIRCLE == circleCount) {
                    mStartingDet = true
                    mStartingDetCount = 0
                }
            } else {
                if (MAX_CIRCLE == circleCount) {
                    ++mStartingDetCount

                    if (mStartingDetCount >= MIN_CIRCLE) {
                        mIsBeginning = true
                        mIsRegStart = false
                        mRegCount = 0
                    }
                } else {
                    mStartingDet = false
                }
            }
        } else {
            if (!mIsRegStart) {
                if (circleCount > 0) {
                    mRegValue = circleCount
                    mRegedChar = Common.INDEX[circleCount]
                    mIsRegStart = true
                    mRegCount = 1
                }
            } else {
                if (circleCount == mRegValue) {
                    ++mRegCount

                    if (mRegCount >= MIN_CIRCLE) {
                        // ok
                        val intelval = if (mRegIndexLast > idx) idx else idx - mRegIndexLast
                        if (mRegValue != mPreRegCircle || intelval > 450) {
                            mRegIndexLast = idx
                            if (mRegedChar == mRegedCharLast) {
                                if (++mRegedCharCount >= MIN_CIRCLE_REPEAT) {
                                    if (++mRegedCharAddedCount < 3) {
//                                        println("==$mRegedCharAddedCount==$mRegedChar")
                                        mListener?.onRecognition(mRegedChar)
                                        mRegedCharCount = 0
                                    }
                                }
                            } else {
                                mRegedCharCount = 0
                                mRegedCharAddedCount = 0
                            }
                            mRegedCharLast = mRegedChar
                            mPreRegCircle = mRegValue
                        }

                        mIsRegStart = false
                    }
                } else {
                    mIsRegStart = false
                }
            }
        }
    }

}
